Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: require content-type parser to set content-type #423

Conversation

SgtPooki
Copy link
Member

@SgtPooki SgtPooki commented Feb 7, 2024

Title

feat: require content-type parser to set content-type

Description

  • adds contentTypeParser function to createVerifiedFetch options & implements it.
  • renamed getStreamAndContentType to getStreamFromAsyncIterable that now returns a stream with the firstChunk seen, so we can pass it to the contentTypeParser function.
  • updates tests in packages/verified-fetch & packages/interop
  • updates packageDocumentation with example

Related #416
Fixes #422

Notes & open questions

  • feat: require content-type parser to set content-type
  • test: remove vFetch interop content-type assertions
  • docs: add content-type customization example

Change checklist

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if necessary (this includes comments as well)
  • I have added tests that prove my fix is effective or that my feature works

@SgtPooki SgtPooki requested a review from a team as a code owner February 7, 2024 19:53
@SgtPooki SgtPooki linked an issue Feb 7, 2024 that may be closed by this pull request
Copy link
Member Author

@SgtPooki SgtPooki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self review

@@ -112,6 +112,26 @@
* const json = await resp.json()
* ```
*
* ### Custom content-type parsing
*
* By default, `@helia/verified-fetch` does not set the `Content-Type` header. This is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected. You can provide a `contentTypeParser` function to the `createVerifiedFetch` function to handle parsing the content type. The function you provide will be passed the first bytes we receive from the network.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could link to #422 if desired.

Suggested change
* By default, `@helia/verified-fetch` does not set the `Content-Type` header. This is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected. You can provide a `contentTypeParser` function to the `createVerifiedFetch` function to handle parsing the content type. The function you provide will be passed the first bytes we receive from the network.
* By default, `@helia/verified-fetch` does not set the `Content-Type` header. This is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected. You can provide a `contentTypeParser` function to the `createVerifiedFetch` function to handle parsing the content type. The function you provide will be passed the first bytes we receive from the network. See https://github.com/ipfs/helia/issues/422 for our thoughts and reasoning.

*
* ```typescript
* import { createVerifiedFetch } from '@helia/verified-fetch'
* import { fileTypeFromBuffer } from '@sgtpooki/file-type'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not referencing mime-types because it only does recognition based on extension, and the troubles @2color mentioned with ipfs-examples/helia-examples#285.

Not referencing file-types because of the lack of browser support, that has been tested and verified in https://github.com/SgtPooki/file-type

* routers: ['http://delegated-ipfs.dev'],
* contentTypeParser: async (bytes) => {
* // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition
* return fileTypeFromBuffer(bytes)?.mime ?? 'application/octet-stream'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* return fileTypeFromBuffer(bytes)?.mime ?? 'application/octet-stream'
* return (await fileTypeFromBuffer(bytes))?.mime ?? 'application/octet-stream'

* bytes we receive from the network, and should return a string that will be used as the value for the `Content-Type`
* header in the response.
*/
contentTypeParser?: ContentTypeParser
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add contentTypeParser to verifiedFetch options? I don't think we need to allow passing it for each call, but we could. LMK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not in the first instance to keep it simple? We can always add it later.

* bytes we receive from the network, and should return a string that will be used as the value for the `Content-Type`
* header in the response.
*/
contentTypeParser?: ContentTypeParser
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to accept an options param here that allows for the VerifiedFetchInit options and just pass that through? That seems better and less prone to CreateVerifiedFetchWithOptions growing unnecessarily large.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I think maybe we want to create a separation between the createVerifiedFetch options arg type and the VerifiedFetch class init arg type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats why I went with this initially. we could always update if we really need to, but i don't imagine createVerifiedFetch init args will vary much

Comment on lines -10 to +12
let verifiedFetch: Awaited<ReturnType<typeof createVerifiedFetch>>
let verifiedFetch: VerifiedFetch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is a bit simpler.

Copy link
Member

@achingbrain achingbrain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

I made a few changes, please comment if you disagree with them:

  1. Split the content type parser tests into their own file
  2. Defaulted to a Content-Type of application/octet-stream instead of not setting one - I think this would be more consistent with what a web server would do
  3. Added tests that show how to use @sgtpooki/file-type and also magic-bytes.js as they both work in browsers
  4. Allows the contentTypeParser to be sync as well as async, and to also return undefined in which case we fall back to application/octet-stream

Also fixed a few bugs:

  1. The contentTypeParser option wasn't being passed through if you used createVerifiedFetch
  2. The interop test had guessing the type of an image as image/png removed - the file is actually image/jpeg with a .png extension :trollface:, I guess this was throwing the previous content-type guesser off - now the test tests for the correct mime type

@achingbrain
Copy link
Member

achingbrain commented Feb 8, 2024

We may wish to also pass the file name to contentTypeParser if it's available? This way if it's passed index.html it can just use the extension to return text/html instead of nothing.

This is done now ✅

Copy link
Member Author

@SgtPooki SgtPooki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm good with the changes


it('can return an image content-type for unixfs pathed data', async () => {
const resp = await verifiedFetch('ipfs://QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR/1 - Barrel - Part 1.png')
// tediously this is actually a jpeg file with a .png extension
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inorite

@achingbrain achingbrain merged commit f58d467 into main Feb 8, 2024
18 checks passed
@achingbrain achingbrain deleted the 422-feat-customizeable-content-type-parsing-in-heliaverified-fetch branch February 8, 2024 16:39
@achingbrain achingbrain mentioned this pull request Feb 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

feat: customizeable content type parsing in @helia/verified-fetch
3 participants